﻿#!/usr/bin/python
# -*- coding: utf-8 -*-

#
#--------------------------------------------------------------
# filename    : peasplitter.py
# created    : 2005.11.02 kean
#
# To-Do        : 
#    2005.12.27
#        implement output directory support, -d outputdir
#    2006.10.25 
#        ui implementation (wxpython)
#        refactor parameters into a param data structure
#    2006.10.25
#        file join functionality
#
#--------------------------------------------------------------

"""
 PySplit file splitter
 
 Splits a file into numbered pieces.

 User interface : wxpython (gui), command-line

 peasplitter.pyw -s partsize file [-a prefix] [-z suffix]
 peasplitter.pyw -j outfile [file...]

 features:
  - argument checking
  - file overwrite checking
  - part size specified as bytes, K, M or G
 
 requires:
 python2.4
 
 environment:
 python2.4.1, wxPython2.6
 
"""

import os, sys, array, wx

from optparse import OptionParser
from array import array

#--------------------------------------------------------------
_DEBUG = 0

UI_HELPSTRING = """ \
peasplitter (Under development)
Command-line mode is available. try 
peasplitter.py --help
"""

#--------------------------------------------------------------
def main() :
    """ 
    Entry stub 
    """
    if len(sys.argv) > 1 :
        return main_cmdline()
    else :
        return main_gui()

#--------------------------------------------------------------
def main_cmdline() :
    """ 
    Command line bootstrapper 
    """
    strfile, optsize, optpref, optsuff = cmdline_parse_options(sys.argv)
    if _DEBUG: print(strfile + " " + str(optsize) + " " + str(optpref))
        
        
    if (strfile and len(strfile) and (optsize > 0)) :
        try :
            outfiles = split_file(strfile, optsize, optpref, optsuff)
            print("Wrote files:")
            for f in outfiles : print(f)
            print("File split successful.")

        except SplitFileExistsException, ex:
            if len(ex.files) :
                print("Wrote files:")
                for f in ex.files : print(f)
            print("Error: output file '%s' exists; bailing out."% ex.value)
            print("File split failed.")
    else :
        print("Some arguments may be missing or incorrect. Please check the input:\n")
        print("filename : " + strfile)
        print("prefix   : " + optpref)
        print("suffix   : " + optsuff)
        print("size     : " + str(optsize))
        print("Please try '%s -h ' for help.\n"% sys.argv[0])

#--------------------------------------------------------------
def main_gui() :
    """ 
    GUI bootstrapper 
    """
    make_optionparser().print_help()
    
    wapp = wx.PySimpleApp()
    frame = MainFrame()
    frame.Show()
    wapp.MainLoop()
    
#--------------------------------------------------------------
def cmdline_parse_options(optargs) :
    """ 
    Command line parse procedure 
    """
    optionp = make_optionparser()
    popts, pargs = optionp.parse_args(optargs)
    if _DEBUG: print (popts)

    # get number part
    # e.g. 640K --> 640
    nsize = 0
    if (popts.size) :
        doscale = 0
        for i in range(len(popts.size)) :
            if not popts.size[i] in "0123456789" :
                if i > 0:
                    nsize = int(popts.size[:i])
                    doscale = 1
                break
        else :
            nsize = int(popts.size)
            
        if _DEBUG: print ("cmdline_parse_options    : " + str(nsize) + " " + str(i) + " " + str(len(popts.size)))
            
        # get multiplier
        # e.g. 640K : K --> 640 * 1024
        if doscale:
            scalef = "KMG"
            scalea = popts.size[i].upper()
            exp = scalef.find(scalea) + 1
            if exp > 0 : nsize *= (1024 ** exp)
    
    # output opts
    strfile = ""
    if len(pargs) > 1: strfile = pargs[1]
    optpref = popts.pref
    if (not optpref) : optpref = strfile
    
    optsuff = popts.suff
    if (not optsuff) : optsuff = ""
        
    if (not (len(strfile) or nsize)) :
        optionp.print_help()
        
    return strfile, nsize, optpref, optsuff

#--------------------------------------------------------------
def split_file(strfile, optsize, optpref, optsuff) :
    """ 
    File split core procedure 
    """
    filesize = os.stat(strfile).st_size

    # read only, binary file
    srcfile = file(strfile, "rb")

    part = 0
    psize = optsize 
    splitname = None
    outfiles = []
    
    try:
        while filesize > 0 :
            splitname = make_filename(optpref, part, optsuff)
            if os.path.exists(splitname) :
                raise SplitFileExistsException(splitname, outfiles)
                
            destfile = file(splitname, "wb")
            if (psize > filesize) : psize = filesize
            # BYTE buffer
            buffile = array('B')
            buffile.fromfile(srcfile, psize)
            buffile.tofile(destfile)
            destfile.close()
            filesize -= psize
            part += 1
            outfiles.append(splitname)
        
    finally:
        srcfile.close()
            
    return outfiles

#--------------------------------------------------------------
def make_filename(optpref, part, optsuff) :
    """ 
    Assemble output filename 
    """
    return "%(pref)s%(part)03d%(suff)s"%{"pref":optpref, "part":part, "suff":optsuff}

#--------------------------------------------------------------
def make_optionparser() :
    """ 
    Prepare command line option parser 
    """
    optionp = OptionParser()
    optionp.set_usage("\n  %prog -s SIZE[k:m:g] filename [-a PREFIX] [-z SUFFIX]")
    optionp.set_description("Splits the input file into smaller parts, numbered prefix000suffix, prefix001suffix and so on. "
                            "By default, the output files are prefixed with the original filename. "
                            "Running %prog without any arguments will start the gui."
                            )
    optionp.add_option("-s", "--size", dest="size", help="size of part in bytes. NNNk, NNNm, or NNNg may be used as multipliers for sizes of 1024, 1024^2 and 1024^3 respectively.")
    optionp.add_option("-a", "--prefix", dest="pref", metavar="PREFIX", help="output file prefix e.g. PREFIX000suffix. By default, the input filename is used as a prefix.")
    optionp.add_option("-z", "--suffix", dest="suff", metavar="SUFFIX", help="output file suffix, e.g. prefix000SUFFIX. By default, the suffix is omitted.")
    return optionp

#--------------------------------------------------------------
class SplitFileExistsException(Exception) :
    """ 
    Exception while splitting file 
    """
    def __init__(self, value, files) :
        self.value = value
        self.files = files
        
#--------------------------------------------------------------

class MyFileDropTarget(wx.FileDropTarget) :
	"""
	Adaptor class for Drop target
	"""
	def __init__(self, fnOnDropFiles) :
		wx.FileDropTarget.__init__(self)
		self.__fn_dropfiles = fnOnDropFiles
		
	def OnDropFiles(self, x, y, filenames) :
		self.__fn_dropfiles(filenames[0])
		
ID_BTN_OK = wx.NewId()
ID_BTN_FILE_OPEN = wx.NewId()
ID_TXT_FILE_OPEN = wx.NewId()

class MainFrame(wx.Frame) :
    """ 
    Main window class 
    """
    def __init__(self) :
        super(MainFrame, self).__init__(None, title="peasplitter (Under development)")
        # panel
        panel = wx.Panel(self, -1)
        #text
        stat = wx.StaticText(panel, label=UI_HELPSTRING, pos=(100,10))
        # icon
        bmp = wx.Image("peasplitter.png").ConvertToBitmap()
        wx.StaticBitmap(panel, bitmap=bmp, pos=(10, 10))
        # OK button
        btnOk = wx.Button(panel, ID_BTN_OK, "OK", (300, 150))
        btnOk.SetDefault()
        btnOk.SetSize(btnOk.GetBestSize())
        self.Bind(wx.EVT_BUTTON, self.onOk, btnOk)
        # FILE_OPEN button
        btnFileOpen = wx.Button(panel, ID_BTN_FILE_OPEN, "Choose file", (300, 80))
        btnFileOpen.SetSize(btnFileOpen.GetBestSize())
        self.Bind(wx.EVT_BUTTON, self.onFileOpen, btnFileOpen)
        
        # FILE_OPEN textbox
        self.txtFileOpen = wx.TextCtrl(panel, ID_TXT_FILE_OPEN, pos=(100, 80), size=(200 - 10, 22))
        self.txtFileOpen.SetDropTarget(MyFileDropTarget(self.onFileDrop))
        
        # bind CLOSE event
        self.Bind(wx.EVT_CLOSE, self.onClose)
        
        # set focus
        btnFileOpen.SetFocus()

    def onFileDrop(self, text) :
        self.__setSplitFile(text)
        
    def onFileOpen(self, event) :
        dlgFile = wx.FileDialog(self, message="Split file", defaultDir="", defaultFile="", 
                        wildcard="All files|*.*", style = wx.OPEN)
        if (wx.ID_OK == dlgFile.ShowModal()) :
            self.__setSplitFile(dlgFile.GetPath())


    def __setSplitFile(self, filename) :
        self.__split_filename = filename
        self.txtFileOpen.SetValue(filename)
        self.txtFileOpen.SetInsertionPointEnd()
        self.txtFileOpen.SetFocus()
        self.__split_filesize = os.stat(filename).st_size
        print("filesize=%d"%self.__split_filesize)

    def onOk(self, event) :
        self.Close(True);
        
    def onClose(self, event) :
        self.Destroy();


#--------------------------------------------------------------
#
# entry point
#
if __name__ == "__main__" :
    main()
